#include "include/sl811hst.h"
#include "../include/usb_host.h"

#include "core/include/aks_processor.h"
#include "core/include/types.h"


extern DWORD SL811HST_BASE_ADR;
extern DWORD SL811HST_ADR;
extern DWORD SL811HST_DAT;


#define CS_CLEAR() (RAMPZ = 0)

void SL811_write(BYTE tgtAddress, BYTE dat)
{
	CS_CLEAR();

	EXTBUS_write(SL811HST_ADR, tgtAddress);

	CS_CLEAR();

	EXTBUS_write(SL811HST_DAT, dat);

	CS_CLEAR();

	return;
}

BYTE SL811_read(BYTE tgtAddress)
{
	CS_CLEAR();

	EXTBUS_write(SL811HST_ADR, tgtAddress);

	CS_CLEAR();

	BYTE dat = EXTBUS_read(SL811HST_DAT);

	CS_CLEAR();

	return dat;
}

void SL811_burstWrite(BYTE baseAdr, BYTE *buf, DWORD size)
{
	if( size <= 0 )
	{
		return;
	}


	for(DWORD i=0 ; i<size ; i++)
	{
		SL811_write(baseAdr+i, buf[i]);
	}

	return;
}

void SL811_burstRead(BYTE baseAdr, BYTE *buf, DWORD size)
{
	if( size<=0 )
	{
		return;
	}

	for(DWORD i=0 ; i<size ; i++)
	{
		buf[i] = SL811_read(baseAdr+i);
	}

	return;
}



BYTE SL811_readData(BYTE deviceAddress, BYTE *dat, BYTE size)
{
	BYTE sts = 0;

	if( size == 0 ){
		return 0;
	}

	SL811_write(SL811_HOST_BASE_ADDRESS_A, 	 0x10 );
	SL811_write(SL811_HOST_BASE_LENGTH_A, 	 size );
	SL811_write(SL811_HOST_DEVICE_ADDRESS_A, deviceAddress );
	SL811_write(SL811_HOST_PID_DDEVICE_EP_A, PID_IN);

	for(BYTE i = 0; i < 200 ; ++i)
	{
		SL811_write(SL811_HOST_CONTROL_REGISTER_A, bARM | bENABLE | bDATA_TOGGLE );

//		TIMER0_resetTimer(2, boardID);
//		while(TIMER0_getTick(boardID));

		while( (SL811_read(SL811_INT_STATUS_REGISTER) & bUSB_A) == 0 );

		sts = SL811_read( SL811_HOST_PID_USB_STATUS_A);

		if( (sts&SL811_NAK) == 0 )
		{
			break;
		}
	}

	if( (sts&SL811_ACK) == SL811_ACK )
	{
		SL811_burstRead( 0x10, dat, size);
	}

	return sts;
}


BYTE SL811_SETUP_transaction(BYTE speed, BYTE usbAdr, BYTE EPx, BYTE *setupPacket, WORD len)
{
	// write setup packet to ram on SL811
	SL811_burstWrite(SL811_MEMORY_BUFFER_BASE_ADR, setupPacket, len);

	// control SL811
	BYTE cmd = bARM | bENABLE | bDIRECTION | ((speed==USB_FULLSPEED) ? bSYNC_SOF : 0) ;

	SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_SETUP 			);
	SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr							);
	SL811_write(SL811_HOST_BASE_ADDRESS_A,		SL811_MEMORY_BUFFER_BASE_ADR	);
	SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)len						);
	SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
	SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);

	// check ACK
	BYTE result =0;
	while(1)
	{
		//wait for done bit
		BYTE int_sts = 0;
		while( !(int_sts&bUSB_A) )
//		do
		{
			int_sts = SL811_read(SL811_INT_STATUS_REGISTER);
			if( (int_sts&bINSERT_REMOVE) || (int_sts&bDEV_DETECT_REUSME) )
			{
				return 0;
			}
		}
//		while( !(int_sts&bUSB_A) );


		// check transaction status
		SL811_write(SL811_INT_STATUS_REGISTER,	0xFF);
		result = SL811_read(SL811_HOST_PID_USB_STATUS_A);

		// NACK
		if(result & SL811_NAK)
		{
			// re-try status: if EPx == EP0 then retry
			if(EPx==0)
			{
				SL811_write(SL811_INT_STATUS_REGISTER,		0xFF);
				SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd);

				result = 0;
			}
			else
			{
				return 0;
			}
		}
		// time-out
		else if(result & SL811_TIME_OUT)
		{
//			SL811_write(SL811_INT_STATUS_REGISTER,		0xFF);
//			SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd);
			SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_SETUP 			);
			SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr							);
			SL811_write(SL811_HOST_BASE_ADDRESS_A,		SL811_MEMORY_BUFFER_BASE_ADR	);
			SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)len						);
			SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
			SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);

//			UART_transmitString(UART_DBG, "SL811_SETUP_transaction():5 \r\n");
		}
		// ACK/STALL/OVERFLOW/ERROR
		else
		{
//			UART_transmitString(UART_DBG, "SL811_SETUP_transaction():6 \r\n");
			return result;
		}
	}

	// return
	return 0;
}


BYTE SL811_OUT_transaction(BYTE speed, BYTE usbAdr, BYTE EPx, BYTE dataX, BYTE *buf, WORD len)
{
	WORD datLen = (len <= 8) ? len : 8;
	WORD size = len - datLen;
	WORD datPtr = 0;

	// control SL811
	BYTE cmd = bARM | bENABLE | bDIRECTION | ((speed==USB_FULLSPEED) ? bSYNC_SOF : 0) ;
	cmd |= dataX;

	BYTE sl811_ramAdr = SL811_HOST_BASE_ADDRESS_A + (((cmd&bDATA_TOGGLE) ==0) ? 0 : 64);

	// write setup packet to ram on SL811
	SL811_burstWrite(sl811_ramAdr, buf+datPtr, datLen);

	SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_OUT 			);
	SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr							);
	SL811_write(SL811_HOST_BASE_ADDRESS_A,		sl811_ramAdr					);
	SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen					);
	SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
	SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);

	// check ACK
	unsigned char result =0;
	while(1)
	{
		//wait for done bit
		BYTE int_sts = 0;
		while( !(int_sts&bUSB_A) )
//		do
		{
			int_sts = SL811_read(SL811_INT_STATUS_REGISTER);
			if( (int_sts&bINSERT_REMOVE) || (int_sts&bDEV_DETECT_REUSME) )
			{
				return 0;
			}
		}
//		while( !(int_sts&bUSB_A) );



		// check transaction status
		SL811_write(SL811_INT_STATUS_REGISTER,	0xFF);
		result = SL811_read(SL811_HOST_PID_USB_STATUS_A);

		// NACK
		if(result & SL811_NAK)
		{
			// re-try status
			if(EPx==0)
			{
//				SL811_write(SL811_INT_STATUS_REGISTER,		0xFF);
//				SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd);
				SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_OUT 			);
				SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr							);
				SL811_write(SL811_HOST_BASE_ADDRESS_A,		sl811_ramAdr					);
				SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen					);
				SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
				SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);

				result = 0;
			}
			else
			{
				return 0;
			}
		}
		else if(result & SL811_TIME_OUT)
		{
//			SL811_write(SL811_INT_STATUS_REGISTER,		0xFF);
//			SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd);
			SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_OUT 			);
			SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr							);
			SL811_write(SL811_HOST_BASE_ADDRESS_A,		sl811_ramAdr					);
			SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen					);
			SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
			SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);
		}
		else if( (result & SL811_ACK) && size != 0)
		{
			cmd ^= bDATA_TOGGLE;
			sl811_ramAdr = SL811_HOST_BASE_ADDRESS_A + (((cmd&bDATA_TOGGLE) ==0) ? 0 : 64);
			datPtr += datLen;
			datLen = (size <= 8) ? size : 8;
			size = size - datLen;

			SL811_burstWrite(sl811_ramAdr, buf+datPtr, datLen);

			SL811_write(sl811_ramAdr,					SL811_MEMORY_BUFFER_BASE_ADR	);
			SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen					);
			SL811_write(SL811_INT_STATUS_REGISTER,		0xFF							);
			SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd								);

		}
		// ACK/STALL/OVERFLOW/ERROR
		else
		{
			return result;
		}
	}

	// return
	return 0;
}


BYTE SL811_IN_transaction(BYTE speed, BYTE usbAdr, BYTE EPx, BYTE dataX, BYTE *buf, WORD len)
{
	WORD datLen = (len > 8) ? 8 : len;
	WORD remainder = len - datLen;
	WORD offset = 0;
	BYTE  bufAdr = SL811_MEMORY_BUFFER_BASE_ADR;

	// control SL811
	BYTE cmd = bARM | bENABLE | ((speed==USB_FULLSPEED) ? bSYNC_SOF : 0) ;
	cmd |= dataX;
	bufAdr = (cmd & bDATA_1)!=0 ? SL811_MEMORY_BUFFER_BASE_ADR + 64 : SL811_MEMORY_BUFFER_BASE_ADR;

	SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_IN	);
	SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr				);
	SL811_write(SL811_HOST_BASE_ADDRESS_A,		bufAdr				);
	SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen		);
	SL811_write(SL811_INT_STATUS_REGISTER,		0xFF				);
	SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd					);

	// check ACK
	unsigned char result =0;
	while(1)
	{
		//wait for done bit
		BYTE int_sts = 0;
		while( !(int_sts&bUSB_A) )
//		do
		{
			int_sts = SL811_read(SL811_INT_STATUS_REGISTER);
			if( (int_sts&bINSERT_REMOVE) || (int_sts&bDEV_DETECT_REUSME) )
			{
				return 0;
			}
		}
//		while( !(int_sts&bUSB_A) );

    	// check transaction status
		SL811_write(SL811_INT_STATUS_REGISTER,	0xFF);
		result = SL811_read(SL811_HOST_PID_USB_STATUS_A);

		// NACK
		if(result & SL811_NAK)
		{
			// re-try status
			if(EPx==0)
			{
				SL811_write(SL811_HOST_PID_USB_STATUS_A,	(EPx&0x0F) | PID_IN	);
				SL811_write(SL811_HOST_DEVICE_ADDRESS_A,	usbAdr				);
				SL811_write(SL811_HOST_BASE_ADDRESS_A,		bufAdr				);
				SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen		);
				SL811_write(SL811_INT_STATUS_REGISTER,		0xFF				);
				SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd					);

				result = 0;
			}
			else
			{
				return 0;
			}
		}
		// ACK
		else if(result & SL811_ACK)
		{
			SL811_burstRead(bufAdr, buf + offset, datLen);

			if(remainder > 0)
			{
				offset += datLen;
				datLen = (remainder > 8) ? 8 : remainder;
				remainder -= datLen;
				cmd ^= bDATA_TOGGLE;
				bufAdr = (cmd & bDATA_1)!=0 ? SL811_MEMORY_BUFFER_BASE_ADR + 64 : SL811_MEMORY_BUFFER_BASE_ADR;

				SL811_write(SL811_HOST_BASE_ADDRESS_A,		bufAdr		);
				SL811_write(SL811_HOST_BASE_LENGTH_A,		(BYTE)datLen);
				SL811_write(SL811_INT_STATUS_REGISTER,		0xFF		);
				SL811_write(SL811_HOST_CONTROL_REGISTER_A,	cmd			);
			}
			else
			{
				return result;
			}
		}
		//STALL/OVERFLOW/ERROR
		else
		{
			return result;
		}
	}

	// return
	return 0;
}



BYTE SL811HST_deviceCheck(void)
{
	BYTE sts, now;

	/* Clear Intrrupt Flag */
	SL811_write(SL811_INT_STATUS_REGISTER, 0xFF);
 	// wait for 10msec
 	now = TIMER_systemTick();
 	while((TIMER_systemTick() - now) < 10);

	sts = SL811_read(SL811_INT_STATUS_REGISTER);
	BYTE ret = 0;

	/* device check */
	if(( sts & bDEV_DETECT_REUSME ))
	{
		return	0;
	}
	else
	{
		return 1;
	}

	return ret;
}

BYTE SL811_getSpeed(void)
{
	BYTE ret = 0;
	BYTE sts = SL811HST_deviceCheck();

	/* device check */
	if(( sts & bDEV_DETECT_REUSME ))
	{
		return	0;
	}
	else
	{
		if(( sts & bD_PLUS ))
		{
			SL811_write(SL811_CONTROL_REGISTER_2, 0xAE);
			SL811_write(SL811_SOF_COUNTER_LOW, 0xE0);
			SL811_write(SL811_CONTROL_REGISTER_1, 0x04 | bSOF_ENABLE);

			ret =  USB_FULLSPEED;
		}
		else
		{
			SL811_write(SL811_CONTROL_REGISTER_2, 0xEE);
			SL811_write(SL811_SOF_COUNTER_LOW, 0xE0);
			SL811_write(SL811_CONTROL_REGISTER_1, bUSB_SPEED | bSOF_ENABLE);

			ret = USB_LOWSPEED;
		}
	}

	SL811_write(SL811_HOST_PID_DDEVICE_EP_A, PID_SOF);
	SL811_write(SL811_TRANSFER_COUNT_A, 0x00);
	SL811_write(SL811_HOST_CONTROL_REGISTER_A, bARM);

	SL811_write(SL811_INT_STATUS_REGISTER, 0xff);

	return ret;
}

void SL811_USB_Reset(void)
{
	BYTE tmp = SL811_read(SL811_CONTROL_REGISTER_1);
 	SL811_write(SL811_CONTROL_REGISTER_2, 0xAE);
	SL811_write(SL811_CONTROL_REGISTER_1, 0x08 | tmp);

  	// wait for 60msec
  	WORD now = TIMER_systemTick();
  	while((TIMER_systemTick() - now) < 60);

  	SL811_write(SL811_CONTROL_REGISTER_1, tmp);
 	SL811_write(SL811_INT_ENABLE_REGISTER, bSOF_RECEIVED | bUSB_RESET | bEP0_DONE);

	return;
}

